package com.straw.hat.net.netty.principle;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;


public class NettyServer {

    /*
    * 1.详解了服务端启动流程 (服务端Channel创建 初始化 线程和Pipeline分配 连接事件的注册 客户端新连接进入,客户端Channel的创建 初始化 线程和Pipeline分配 读事件的注册)
    * */
    public static void start(Integer port) throws InterruptedException {
        /*boss线程组*/
        EventLoopGroup boos = null;
        /*work线程组*/
        EventLoopGroup work = null;
        /*Netty引导类*/
        ServerBootstrap bootstrap;
        try {

            /*初始化线程池 默认创建核心线程数*2个线程
            *
            * 1.创建线程池ThreadPerTaskExecutor
            * 2.for循环创建this.newChild (NioEventLoop){
            *       this.provider = selectorProvider;
            *       this.selector = this.openSelector();
            *       this.selectStrategy = strategy;
            *       this.executor = ThreadPerTaskExecutor;
            * } (每个Event都对应一个selector,并且自定SelectorKey的结构为数组)
            * 3.创建chooserFactory.newChooser(this.children)
            * */
            boos = new NioEventLoopGroup();
            /*同上
            * */
            work = new NioEventLoopGroup();
            bootstrap = new ServerBootstrap();
            bootstrap.group(boos, work)
                    /*
                    * 指定服务端的Channel是NioServerSocketChannel底层采用反射的机制创建
                    * */
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel channel) throws Exception {
                            channel.pipeline().addLast(new ServerHandler());
                        }
                    });
            /*
            * netty启动流程bind()
            *   --> doBind() ---> {
            *           initAndRegister() {
            *               this.channelFactory.newChannel() [创建服务端的NioServerSocketChannel] {
            *                           使用一开始指定的NioServerSocketChannel
            *                           调用底层的NIO创建Channel这个Channel
            *                           初始化默认的Pipe初始化默认的头尾ChannelHandle
            *                       }
            *              init()[初始化Channel]{
            *                     1.设置Option和Attribute
            *                     2.配置Pipe(Head --- ServerBootstrapAcceptor --- tail)
            *                     3.设置ServerBootstrapAcceptor(处理新连接的时候){
            *                        ch.eventLoop().execute(new Runnable() {
            *                        public void run() {
            *                        pipeline.addLast(new ChannelHandler[]{new ServerBootstrap.ServerBootstrapAcceptor(currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs)});
            *                            }
            *                        });
            *
            *                           }
            *                         }
            *              this.config().group().register() [注册Channel到Selector] {
            *                   使用第一步创建的EvenLoopGroup中的EventLoop绑定当前Channel实现一个Channel只会被一个线程处理
            *                   --->register0注册 (这里区分是InEventLoop内部线程调用还是外部线程调用(客户端进来的线程)*走的异步任务*,
            *                              eventLoop.execute(new Runnable() {
            *                                        public void run() {
            *                                AbstractUnsafe.this.register0(promise);
            *                              }
            *                        });
            *                       ){
            *                           ---->doRegister调用JDK底层注册Selector 但是不注册任何事件的关注 仅仅只是注册
            *                               --->AbstractChannel.this.pipeline.invokeHandlerAddedIfNeeded();触发事件
            *                              --->AbstractChannel.this.pipeline.fireChannelRegistered(); 触发注册成功的事件
            *                   }
            *             }
            *           }
            *          *channel.eventLoop().execute(Task) {
            *               这个Task工作就是
            *               1doBind0()----> AbstractChannel.this.doBind(localAddress); JDK底层的绑定(一路跟踪到AbstractChannel的AbstractUnsafe的bind的方法)
            *               2AbstractChannel.this.pipeline.fireChannelActive();触发Channel绑定激活成功的事件
            *                       最终走到DefaultChannelPipeline的HeadContext的
            *                       1.ctx.fireChannelActive();
            *                       2.this.readIfIsAutoRead();(最终会调用到AbstractChannel的AbstractUnsafe的beginRead的方法){
            *                           selectionKey.interestOps(interestOps | this.readInterestOp);(readInterestOp是创建NioServerSocketChannel设置的Accept事件,注册Accept事件)
            *                       }
            *               }
            *             }(这个channel.eventLoop().execute()就是调用SingleThreadPool.this.run()会触发启动EventLoop的过程,所以接下去就是分析NioEventLoop的run方法
            *               1. selector (检测所有的事件){}
            *               2. processKeys (解决空轮询Bug,盘点各种Key的操作类型,同时也是新连接接入检测的入口和处理入口){
            *                    if ((readyOps & SelectionKey.OP_READ) != 0 || readyOps == SelectionKey.OP_ACCEPT)
            *                        unsafe.read();(由AbstractNioMessageChannel中的NioMessageUnsafe的read处理) {
            *                                 --->doReadMessages (创建客户端Channel){
            *                                        buf.add(new NioSocketChannel(this, ch));***创建客户端Channel*** {
            *                                              创建Pipeline设置TcpNoDelay
            *                                        }
            *                                   }
            *                                --->pipeline.fireChannelRead(this.readBuf.get(i)); (传递读取到的客户端会执行服务端Pipeline中的ServerBootstrapAcceptor的handle,进行NioEventLoop和Selector的绑定){
            *                                       1.--->child.pipeline().addLast(new ChannelHandler[]{this.childHandler});把用户自定义的Handle添加进去
            *                                       2.--->设置Options和Attributes
            *                                       3.--->this.childGroup.register(child).addListener(new ChannelFutureListener() {
            *                                               public void operationComplete(ChannelFuture future) throws Exception {
            *                                                    if (!future.isSuccess()) {
            *                                                            ServerBootstrap.ServerBootstrapAcceptor.forceClose(child, future.cause());
            *                                                    }
            *                                              }
            *                                               --->---->doRegister调用JDK底层注册Selector 但是不注册任何事件的关注 仅仅只是注册
            *                                               4.读事件的注册beginRead
            *                                         });(进行NioEventLoop和Selector的绑定,使用的childGroup就是Works的EventLoopGroup)
            *
            *                                   }
            *                        }
            *               }
            *               3. runTask (合并定时任务,通过执行任务实现异步无锁化,所以业务端的ChannelHandle都是线程安全的){}
            *            )
            *   }
            *   sync() (future的sync阻塞等待完成在进行下一步操作)
            * */
            ChannelFuture future = bootstrap.bind(7777).sync();
            future.channel().closeFuture().sync();
            /*
            *2 Pipeline详解
            * */
            /*
            *3 ByteBuf详解
            * */
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (boos != null && work != null) {
                boos.shutdownGracefully().sync();
                work.shutdownGracefully().sync();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {

        start(8);
    }
}
